"use strict";

var _bbg_createDefaultOptions = createDefaultOptions;
var _wrappedAxisRender = Highcharts.Axis.prototype.render;

function BBGAxis() {
  this.FORMAT_DAILY = "%d %b %Y";
  this.FORMAT_DAILY_SHORT = "%d %b";
  this.FORMAT_MONTHLY = "%b %Y";
  this.FORMAT_YEARLY = "%Y";
  this.PIXELS_PRIMARY_TICK_OVERLAP = 25;
  this.PIXELS_GENERATE_ONE_TICK = 53

  //dont have time to calculate them..these are guesses
  this.progressions = [
  [ 10, 12, 14 ],
  [ 11, 14 ],
  [ 10, 15 ],
  [ 10, 14 ],
  [ 9, 14 ],
  [ 12],
  [ 14],
  [ 13],
  [ 9] 
  ];

  //key is the minimum space to start 
  this.autoTicks = new AutoTicks( {
    30: 0,
    70: 1,
    100: 2,
    150: 3,
    175: 4
  } );

  //when to start trimming the label without the year in case one day has 
  //a lot less data than the others
  this.FORMAT_SHORT_START = 58;

  //Any pixels per day smaller than this number triggers short
  this.PIXELS_DAILY_SHORT_START = 70;
  
  //how small do we get to start two days
  this.PIXELS_DAILY_TWODAY_START = 60;


}

var _bbgAxis = new BBGAxis();

createDefaultOptions = function() {
  var options = _bbg_createDefaultOptions.apply(this, arguments);

  options.xAxis.push(
    { // slave axis
      id: "bbgAxis",
      crosshair: false,
      type: 'datetime',
      linkedTo: 0,
      startOnTick: true,
      endOnTick: true,
      lineWidth: 0,
      showFirstLabel: true,
      showLastLabel: false,
      gridLineWidth: 1,
      gridLineDashStyle: "LongDash",
      gridLineColor: options.xAxis[0].labels.style.color,
      tickPositioner: bbgTickPositioner,
      labels: {
        autoRotation: 0,
        align: 'left',
        y: 9,

        style: options.xAxis[0].labels.style,
        formatter: function () {
          var formatter = this.axis.bbgAxisFormat;
          var positions = this.axis.tickPositions;
          if ( !isDefined( formatter ) ) {
            formatter = _bbgAxis.FORMAT_DAILY;
          }
          if ( _bbgAxis.isCustomViewActive( this.axis ) && formatter != _bbgAxis.FORMAT_DAILY_SHORT ) {
            if ( isValued( positions.info ) && isValued( positions.info.nextPosition ) ) {
              var nextPosition = positions.info.nextPosition[this.value];
              var currentPosition = this.axis.toPixels( this.value );
              var delta = nextPosition - currentPosition;
              if ( delta < _bbgAxis.FORMAT_SHORT_START )  {
                formatter = _bbgAxis.FORMAT_DAILY_SHORT;
              }
            }
          }
          return Highcharts.dateFormat( formatter, this.value) ;
        }
      }
    }
  );
  _bbgAxis.disableTickPromotion( options );
  options.xAxis[0].labels.autoRotation = false;
  options.xAxis[0].labels.formatter = primaryAxisFormatter;
  options.xAxis[0].dateTimeLabelFormats = {
    millisecond: '%H:%M:%S.%L',
    second: '%H:%M:%S',
    minute: '%H:%M',
    hour: '%H:%M',
    day: '%e',
    week: '%e',
    month: '%b',
    year: '%Y'
  };

  return options;
};



/**
 * Place holder if we want to customize the formatting of the primary labels
 */
function primaryAxisFormatter() {
  var axis = this.axis;
  var value = this.value;
  if ( isValued( axis.tickPositions.info ) && axis.tickPositions.info.autoTicks == true) {
    var workingTime = new Date( value );
    var minutes = workingTime.getMinutes();
    var seconds = workingTime.getSeconds();
    var difference  = seconds % 60;
    var shift = difference < 30 ? -difference : 60 - difference;
    workingTime.setSeconds( seconds + shift );

    difference  = minutes % 30;
    //less than 15 minutes to next half hour, go back to previous
    shift = difference < 15 ? -difference : 30 - difference;
    workingTime.setMinutes( minutes + shift );
    value = workingTime.getTime();
    this.value = value;
  }

  return axis.defaultLabelFormatter.call( this, axis, this.chart, this.isFirst, this.isLast, this.dateTimeLabelFormat, value );
}

function AutoTicks( tickCache ) {
  this.tickCache = tickCache;
  this.pixelSizes = Object.keys( this.tickCache );

  for( var index = 0; index < this.pixelSizes.length; index++ ) {
    this.pixelSizes[ index ] = parseFloat( this.pixelSizes[ index ] );
  }

  this.pixelSizes.sort( function( a , b ) {
    return a - b;
  });

  this.getTickCount = function( pixelSize ) {
    if ( isUndefined( this.pixelSizes ) ) return 0;
    var lowerBound = 0;
    for( var index = 0; index <= this.pixelSizes.length; index++ ) {
      var upperBound =  index == this.pixelSizes.length ? 999999 : this.pixelSizes[ index ];
      if ( pixelSize >= lowerBound && pixelSize < upperBound) {
        var tickIndex = ( index == this.pixelSizes.length ? index - 1 : index );
        return  this.tickCache[ this.pixelSizes[ tickIndex ] ];
      }
      var lowerBound = upperBound;
    }
    return 0;
  }
}


BBGAxis.prototype.isCustomViewActive = function ( axis ) {
  return axis.chart.isManuallyZoomed() == false && axis.isDailyView();
}

BBGAxis.prototype.disableTickPromotion = function( options ) {
  this._originalTickPositioner = options.xAxis[ 0 ].tickPositioner;
  options.xAxis[ 0 ].tickPositioner = xAxisPositioner;
}

/**
 * We're getting rid of all the hour->day promotion for now, all the other promotion we will leave alone  
 * Just display the time, we have the bloomberg axis to handle the date
 */
BBGAxis.prototype.clearNonContiguousPromotion = function( tickPositions, min, max ) {
  if( !isValued( tickPositions ) || !isValued( tickPositions.info ) ) return;
  var mainUnit = tickPositions.info.unitName;
  if ( mainUnit != "hour" && mainUnit != "minute"  && mainUnit != "millisecond") return;
  var totalDayTicks = 0;
  tickPositions.forEach( function( tickPosition ) {
    var promotion = tickPositions.info.higherRanks[ tickPosition ];
    if ( promotion == "day" ) {
      totalDayTicks++;
    }
  });

  var totalTicks = tickPositions.length;
  //dont count the last tick if we put it there
  if ( tickPositions.paddingTick == tickPositions[ tickPositions.length ] ) totalTicks--;

  //if we have more than 3 strays...well good bye day formatting
  if ( totalTicks - totalDayTicks > 3 ) {
    //clear all the midnight->date
    tickPositions.info.higherRanks = {};
  }
}

BBGAxis.prototype.getTickUnits = function( tickPositions ) {
  if( !isValued( tickPositions ) || !isValued( tickPositions.info ) ) return "millisecond";
  var mainUnit = tickPositions.info.unitName;
  if ( !isValued( tickPositions.info.higherRanks ) ) return mainUnit;
  var higherRanksPositions = Object.keys( tickPositions.info.higherRanks );
  if ( higherRanksPositions.length == 0 ) return mainUnit;

  return tickPositions.info.higherRanks[ higherRanksPositions[ 0 ] ];
}

BBGAxis.prototype.createTicksBetweenDays = function ( axis, currentDay, nextMidnight, numberOfTicks ) {
  var currentTimes = [];
  var startingPosition = axis.toPixels( Math.max( currentDay, axis.min ) );
  var nextMidnightPosition = axis.toPixels( nextMidnight );
  //attempt to find the overlap with midnight (last point)

  var incrementLocation =  + ( ( nextMidnightPosition - startingPosition ) / (numberOfTicks + 1 ) );
  for ( var ticksCreated = 1 ; ticksCreated <= numberOfTicks; ticksCreated++ ) {
    var incrementalTime = new Date( axis.toValue( startingPosition + ( ticksCreated * incrementLocation ) ) );

    currentTimes.push( incrementalTime.getTime() );
  }

  return currentTimes;
}

BBGAxis.prototype.isOverlappingPositions = function( first, second ) {
  return Math.abs( second - first ) < 2;
}

/**
 *
 */
BBGAxis.prototype.getAffectedDates = function( axis ) {
  var affectedDates = [];
  if (isUndefined( axis ) || isUndefined( axis.min ) || isUndefined( axis.max ) ) return affectedDates;
  var delta = axis.max - axis.min;
  var days = Math.ceil( delta / DATE_UTILS.MILLIS_PER_DAY );
  var currentDate = new Date( DATE_UTILS.getMidnight( axis.min ) );
  var lastPosition =  0;
  var minPosition = axis.toPixels( axis.min );
  while( currentDate.getTime() < axis.max ) {
    var midnightPosition = Math.max( minPosition, axis.toPixels( currentDate.getTime() ) );

    //no overlaps...eg there is no data there (weekend/holiday)
    if ( lastPosition == 0 || !this.isOverlappingPositions( midnightPosition, lastPosition ) ) {
      var tomorrow = new Date( currentDate.getTime() );
      DATE_UTILS.addDay( tomorrow );
      var tomorrowPosition = axis.toPixels( tomorrow.getTime() );
      while( this.isOverlappingPositions( tomorrowPosition, midnightPosition ) && currentDate.getTime() < axis.max ) {
        DATE_UTILS.addDay( currentDate );
        midnightPosition = axis.toPixels( currentDate.getTime( ) );
        DATE_UTILS.addDay( tomorrow );
        tomorrowPosition = axis.toPixels( tomorrow.getTime() );
      }
      affectedDates.push( { time: currentDate.getTime(), position: midnightPosition, date: new Date( currentDate.getTime() ) });
    }

    DATE_UTILS.addDay( currentDate );
    lastPosition = midnightPosition;
  }


  var pixelsPerDay = (affectedDates.length > 0 ) ? Math.ceil( ( axis.chart.plotWidth ) / affectedDates.length ) : axis.chart.chartWidth;
  affectedDates.twoDayMode = false;
  affectedDates.shortDisplay = false;
  this.log( "**pixels per day", pixelsPerDay, "total days: ", affectedDates.length );
  if ( pixelsPerDay < this.PIXELS_DAILY_TWODAY_START ) {
    this.log( "***Switching to two day:daily mode we only have %d pixels per day [%d]", pixelsPerDay, this.PIXELS_DAILY_TWODAY_START );
    affectedDates.twoDayMode = true;
  } else if ( pixelsPerDay < this.PIXELS_DAILY_SHORT_START ) {
    affectedDates.shortDisplay = true;
    this.log( "***Switching to short day daily mode we have %d pixels per day [%d]", pixelsPerDay,  this.PIXELS_DAILY_SHORT_START );
  }

  return affectedDates;
}


BBGAxis.prototype.log = function( arg1, arg2, arg3, arg4, arg5) {
  var lastLogTime  = this.lastLogTime;
  var now = Date.now();
  if ( isUndefined( lastLogTime ) || now - lastLogTime > 10000 ) {
    logger.log( arg1, arg2, arg3, arg4, arg5 );  
    this.lastLogTime = now;
  }
  
}

BBGAxis.prototype.createDailyTicks = function( axis ) {
  if ( isNotValued( axis ) || isNotValued( axis.min ) || isNotValued( axis.max ) ) return null;
  var generatedTicks = [];
  var currentDate = new Date( axis.min );
  DATE_UTILS.setMidnight( currentDate );
  var endDate = axis.max;
  var endDateMidnight = DATE_UTILS.getMidnight( endDate );

  var affectedDates = this.getAffectedDates( axis );
  var dateIndex = 0;

  var minPosition = axis.toPixels( axis.min );
  var progressionsExamined = this.progressions;

  for ( dateIndex = 0; dateIndex < affectedDates.length; dateIndex++ ) {
    var lastDay = dateIndex == affectedDates.length - 1;
    currentDate = affectedDates[ dateIndex ].time;
    var midnightPosition = affectedDates[ dateIndex ].position;
    //generate ticks for the date range if we are not in two day range or the last day of it
    if ( !affectedDates.twoDayMode || ( lastDay && affectedDates.length % 2 == 1 ) ) {
      this.log( "Midnight position, ", new Date( currentDate ), midnightPosition );

      var nextMidnight = lastDay ? axis.max : affectedDates[ dateIndex + 1 ].time;
      var nextMidnightPosition = axis.toPixels( nextMidnight );      
      if ( lastDay ) {
        nextMidnightPosition = nextMidnightPosition *.98;
      }
      var spaceAvailable = nextMidnightPosition - midnightPosition;
      this.log( "Spacing available: ", spaceAvailable, " Date: ", affectedDates[ dateIndex ].date );
      var autoTickNumber = this.autoTicks.getTickCount( spaceAvailable );
      if ( autoTickNumber > 0 ) {
        generatedTicks.extend ( this.createTicksBetweenDays( axis, currentDate, nextMidnight, autoTickNumber ) );
      }
    } else if ( dateIndex > 0 ) {
      //first tick day wont be displayed in two day mode
      generatedTicks.push( currentDate );
    } 
  }


  if ( generatedTicks.length > 0 ) {
    generatedTicks.info = {
      unitName: "hour",
      unitRange: 3600000,
      higherRanks: {},
      autoTicks: true,
      affectedDates: affectedDates
    }
  }


  return generatedTicks;
}

BBGAxis.prototype.isUnitHourlyOrLess = function( unitName ) {
  return ( unitName != "year" && unitName !="month" && unitName != "day" && unitName !="week" );
}


/**
 * Midnight ticks will be blasted by the bloomberg ticks anyway
 * if we are on hourly mode do we have any day ticks that will be replaced anyway as this function
 * is assumed to be called while in daily mode
 */
BBGAxis.prototype.filterTicks = function( tickPositions ) {
  if ( isUndefined( tickPositions ) || tickPositions.length == 0 ) return 0;
  var filteredTicks = 0;
  var checkForHigherRanks = false;
  if ( isDefined( tickPositions.info ) && this.isUnitHourlyOrLess( tickPositions.info.unitName ) && isDefined( tickPositions.info.higherRanks ) ) {
    checkForHigherRanks = true;
  }

  tickPositions.forEach( function( tickPosition ) {
    //this is local timestamp so have to go date math for it to be easier.
    var date = new Date( tickPosition );
    if ( date.getHours() == 0 || ( checkForHigherRanks && tickPositions.info.higherRanks[ tickPosition ] == "day" ) ) {
      filteredTicks++;
    }
  });

  return tickPositions.length - filteredTicks;
}

function xAxisPositioner() {
  var originalTickPositions = _bbgAxis._originalTickPositioner.apply( this, arguments );
  _bbgAxis.clearNonContiguousPromotion( originalTickPositions, this.min, this.max );


  //manually zoomed we'll rely on highcharts...this is floating window functionality
  //that is purely ours
  if( _bbgAxis.isCustomViewActive( this ) ) {
    var originalTicksFilteredCount = _bbgAxis.filterTicks( originalTickPositions );
    var bbgTickPositions = _bbgAxis.createDailyTicks( this );
    if ( bbgTickPositions != null &&  bbgTickPositions.length >=  originalTicksFilteredCount) {
        if ( isValued( originalTickPositions.info ) && isValued( originalTickPositions.info.paddingTick ) ) {
          //padding tick needs to go back in..we hide the last one
          bbgTickPositions.push( originalTickPositions.info.paddingTick );
        }    
      return bbgTickPositions;
    }
  }

  return originalTickPositions;
}


BBGAxis.prototype.getChartXBounds = function( chart ) {
  var extremes = chart.xAxis[0].getExtremes();
  var minXValue = extremes.userMin;
  var maxXValue = extremes.userMax;

  if ( !isDefined( minXValue) ) {
    minXValue = Number.MAX_VALUE;
    chart.series.forEach( function ( inSeries ) {
      if ( inSeries.xData.length == 0 ) return;
      if ( inSeries.xData[0] < minXValue ) {
        minXValue = inSeries.xData[0];
      }
    }.bind( chart ));
  }
  if ( !isDefined( maxXValue) ) {
    maxXValue = Number.MIN_VALUE;
    chart.series.forEach( function ( inSeries ) {
      if ( inSeries.xData.length == 0 ) return;
      var last = inSeries.xData.length - 1;
      if ( inSeries.xData[ last ] > maxXValue ) {
        maxXValue = inSeries.xData[last];
      }
    }.bind( chart ));
  }

  return { min: minXValue, max: maxXValue };
}


BBGAxis.prototype.fixAxis = function ( bbgAxis ) {
  var originalAxis = bbgAxis.linkedParent;

  if ( !isDefined( bbgAxis.tickPositions ) || bbgAxis.tickPositions.length == 0 ) return;
  this.restorePrimaryTickLabels( originalAxis );
  this.removeOverlappingTicks( bbgAxis, originalAxis );
  this.adjustGroupedTicks( bbgAxis, originalAxis );
  bbgAxis.isDirty = true;
}

/**
 * When resized or ticks recalculated..highcharts doesnt recreate the ticks so lets make
 * sure they are visible if we hid them due to overlapping
 */
BBGAxis.prototype.restorePrimaryTickLabels  = function ( originalAxis ) {
  if ( !isDefined( originalAxis.tickPositions ) ) return;
  originalAxis.tickPositions.forEach( function( tickPosition ) {
    var tick = originalAxis.ticks[ tickPosition ];
    if ( tick.hasOwnProperty( "_bbgHidden" ) ) {
      delete tick._bbgHidden;
      tick.label.show();
      tick.mark.show();
    }
  });
}

/**
 * Sweep through the axis for overlapping labels...brute force, it doesn't always
 * apply it on midnight
 */
BBGAxis.prototype.removeOverlappingTicks  = function ( bbgAxis, originalAxis ) {
  if (!isValued( bbgAxis ) || !isValued( originalAxis ) ) return;
  bbgAxis.tickPositions.forEach( function( tickPosition ) {
    var bbgTick = bbgAxis.ticks[ tickPosition ];
    if ( !isValued( bbgTick ) || !isValued( bbgTick.mark ) ) return;

    var bbgTickMarkX = bbgTick.mark.getBBox().x;
    if ( !isValued( bbgTick ) || !isValued( bbgTick.mark ) ) return;
    for( var originalTickIndex = 0 ; originalTickIndex < originalAxis.tickPositions.length; originalTickIndex++ ) {
      var originalTickPosition = originalAxis.tickPositions[ originalTickIndex ];
      var originalTick = originalAxis.ticks[ originalTickPosition ];
      if ( !isValued( originalTick ) || !isValued( originalTick.mark ) ) continue;
      var originalMark = originalTick.mark;
      var originalMarkX = originalMark.getBBox().x;
      //within 5 pixels of the axis below..hide the label
      if ( Math.abs( originalMarkX - bbgTickMarkX ) < 5 ) {
        originalTick.label.hide();
        originalTick.mark.hide();
        originalTick._bbgHidden = true;
      }
    }
  });
}

BBGAxis.prototype.adjustGroupedTicks = function( bbgAxis, originalAxis ) {
  for ( var index = 0; index < bbgAxis.tickPositions.length - 1 ; index++ ) {
    var position = bbgAxis.tickPositions[ index ];
    //on manual zoom, only look at the stuff within view
    if ( position < bbgAxis.min ) continue;
    var tick = bbgAxis.ticks[ position ];   
    var nextTick = undefined;
    var nextPosition = bbgAxis.tickPositions[ index + 1 ];
    //manual zoom positioning
    if ( nextPosition <= bbgAxis.max ) {
      nextTick = bbgAxis.ticks[ nextPosition ];
    }
    if ( !isDefined( tick ) ) return;
    this.extendBoundaryTicks( tick );
    this.centerTick( tick, nextTick );

    if ( tick.isFirst ) {
      tick.gridLine.hide();
    }
  }
  if ( bbgAxis.tickPositions.length > 1 ) {
    var lastTick =  bbgAxis.ticks [ bbgAxis.tickPositions[ bbgAxis.tickPositions.length - 1 ] ];
    if ( lastTick != null ) {
      lastTick.mark.hide();
      lastTick.gridLine.hide();
    }
  }
}

BBGAxis.prototype.centerTick = function( tick, nextTick ) {
  var chartWidth = tick.axis.chart.chartWidth;
  var nextX = chartWidth;
  if ( isDefined( nextTick ) && !nextTick.isLast) {
    nextX = Math.min(  tick.axis.toPixels( nextTick.pos ), nextX );  
  }

  var currentBox = tick.label.getBBox();
  var currentEndX = tick.axis.toPixels( tick.pos )  + currentBox.width;

  var shift = ( nextX - currentEndX ) / 2;
  var newXLocation = tick.axis.toPixels( tick.pos ) + shift;
  if ( shift > 0 ) {
    tick.label.attr( { 
      "x":  newXLocation
    } );
    //in case they were hidden as the last tick on another zoom
    tick.label.show();
  } else if ( nextX < chartWidth ) {
    //we dont have space..hide the label only
    tick.label.hide();
  }
  
}

/**
 * Adjust the path for the original axis tick to reach the lower axis
 * @param bbgAxis
 */
BBGAxis.prototype.extendBoundaryTicks = function( bbgTick ) {
  if ( !isValued( bbgTick) || !isValued( bbgTick.mark ) ) return;
  var bbgMarkBox = bbgTick.mark.getBBox();
  var x = bbgMarkBox.x;
  var targetBox = bbgTick.axis.linkedParent.axisLine.getBBox();
  var targetY = targetBox.y;
  var endY = bbgMarkBox.y + bbgMarkBox.height;
  var newPath = "M " + x + " " + targetY + " L " + x + " " + endY;
  bbgTick.mark.attr( {
    d: newPath
  } );
}

/**
 * End date is expected to be at midnight for twoDayAdder
 */
BBGAxis.prototype.twoDayAdder = function( date, endDate, axis, lastTickPosition ) {
  var added = 0;
  var previousPosition = lastTickPosition == null ? axis.toPixels( axis.min ) : lastTickPosition;
  var currentPosition = axis.toPixels( date.getTime() );
  //keep adding while we are overlap the previous tick
  while( _bbgAxis.isOverlappingPositions( currentPosition, previousPosition ) && date.getTime() <= endDate.getTime() ) {
    DATE_UTILS.addDay( date );
    previousPosition = currentPosition;
    currentPosition = axis.toPixels( date.getTime() );
  }

  DATE_UTILS.addDay( date );
  previousPosition = currentPosition;
  currentPosition = axis.toPixels( date.getTime() );

  //keep adding while we are overlaping the previous tick - one more time as we need to go past all overlaps
  //this counts as two legitimate adds
  while( _bbgAxis.isOverlappingPositions( currentPosition, previousPosition ) && date.getTime() <= endDate.getTime() ) {
    DATE_UTILS.addDay( date );
    previousPosition = currentPosition;
    currentPosition = axis.toPixels( date.getTime() );
  }  

  //passed the end time
  if ( date.getTime() > endDate.getTime() ) return;

  //does the day i landed on have no data? check for tomorrow's data to see if if lands near the same spot
  //if it does keep checking tomorrow until we dont
  var emptyDateCheck = new Date( date.getTime() );
  DATE_UTILS.addDay( emptyDateCheck );
  previousPosition = currentPosition;
  currentPosition = axis.toPixels( emptyDateCheck.getTime() );


  while( _bbgAxis.isOverlappingPositions( currentPosition, previousPosition ) && date.getTime() <= endDate.getTime() ) {
    DATE_UTILS.addDay( date );
    DATE_UTILS.addDay( emptyDateCheck );
    previousPosition = currentPosition;
    currentPosition = axis.toPixels( emptyDateCheck.getTime() );
  }
}

Highcharts.Axis.prototype.isDailyView = function() {
  if ( isUndefined( this.min ) || isUndefined( this.max ) ) return false;
  var DAY_THRESHOLD = this.chart.getOptionParameter(KEY_BBG_DAILY_THRESHOLD) * 1000;
  return this.max - this.min < DAY_THRESHOLD
}

function bbgTickPositioner() {

  var axis = this;
  if ( axis.linkedParent.series.length == 0 ) return axis.tickPositions;
  if ( isUndefined( axis.bbg ) ) {
    axis.bbg = {
    };    
  }

  var parentTickPositions = axis.linkedParent.tickPositions;
  var currentUnits = _bbgAxis.getTickUnits( parentTickPositions );
  var promotedUnits = "daily";
  if ( !isDefined( this.min ) || !isDefined( this.max ) ) {
    return axis.tickPositions;
  }
  var positions = [];
  
  var resetFunction = DATE_UTILS.setMidnight;
  var addFunction = DATE_UTILS.addDay;
  var formatter =  _bbgAxis.FORMAT_DAILY;
  var twoDay = false;
  if ( isValued( parentTickPositions.info ) && isValued( parentTickPositions.info.affectedDates ) ) {
    if ( parentTickPositions.info.affectedDates.shortDisplay ) formatter = _bbgAxis.FORMAT_DAILY_SHORT;
    twoDay =  parentTickPositions.info.affectedDates.twoDayMode;
  }


  //for daily data, we want to keep it in day, no matter what units highcharts came up with as we'll only show
  //one or two entries for the bbg axis
  if ( !axis.isDailyView() ) {
    if ( currentUnits == "day" || currentUnits == "week" ) {
      formatter = _bbgAxis.FORMAT_MONTHLY;
      resetFunction = DATE_UTILS.setFirstOfMonth;
      addFunction = DATE_UTILS.addMonth;
      promotedUnits = "month";
    } else if ( currentUnits == "month" || currentUnits == "year") {
      formatter = _bbgAxis.FORMAT_YEARLY;
      resetFunction = DATE_UTILS.setFirstOfYear;
      addFunction = DATE_UTILS.addYear;
      promotedUnits = "year";
    } 
    axis.bbg.DAILY = false;   
  } else {
    if ( axis.bbg.DAILY != true ) {
      logger.debug( "Switching to DAILY view as we are within the threshold of %sms, two day mode? %s", axis.chart.getOptionParameter( KEY_BBG_DAILY_THRESHOLD ), twoDay );  
      axis.bbg.DAILY = true;
    }
    if ( twoDay ) {
      addFunction = _bbgAxis.twoDayAdder;
    }

  }


  axis.bbgAxisFormat = formatter;


  if ( axis.bbg.currentUnits != currentUnits || axis.bbg.promotedUnits != promotedUnits ) {
      logger.debug( "BBG Axis, current units: %s bbg axis units: %s", currentUnits, promotedUnits);
      axis.bbg.currentUnits = currentUnits;
      axis.bbg.promotedUnits = promotedUnits;
  }

  positions.info = {
    nextPosition: {},
    higherRanks: {}
  };

  positions.push( this.min );
  var currentDate = resetFunction.call( DATE_UTILS, new Date( this.min ) );
  var endInterval = resetFunction.call( DATE_UTILS, new Date( this.max ) );
  var previousPosition = null;
  addFunction.call( DATE_UTILS, currentDate, endInterval, axis, previousPosition );
  //have to advance forward..that value is already handled by the bounds    
  while( currentDate.getTime() <= endInterval.getTime() ) {
    var currentPosition = axis.toPixels( currentDate.getTime() );
    positions.info.nextPosition[ positions[ positions.length -1] ] = currentPosition;  
    
    //too tight or hack for weekends....
    if ( previousPosition != null &&  _bbgAxis.isOverlappingPositions(  currentPosition, previousPosition ) ) {
      positions.pop();
      delete positions.info.nextPosition[previousPosition];
    }
    positions.push( currentDate.getTime() );
    previousPosition = currentPosition;
    addFunction.call( DATE_UTILS, currentDate, endInterval, axis, previousPosition );
  }
  positions.info.nextPosition[ positions[ positions.length -1] ] = this.chart.chartWidth;  

  //sacrificial lamb to get clipped
  positions.push( this.max );
    return positions;
}



Highcharts.Axis.prototype.render = function() {
  _wrappedAxisRender.apply( this );
  if ( this.options.id != "bbgAxis" ) return;
  _bbgAxis.fixAxis( this );
}